﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Hvam.SharePoint.Search.Language.Visitors
{
    internal class PropertyExtractorExpressionVisitor : ExpressionVisitor
    {
        private List<string> Values { get; set; }
        private string Property { get; set; }

        public Tuple<Expression, List<string>> Extract(Expression expression, string property)
        {
            Property = property.ToUpper();
            Values = new List<string>();

            var e = Visit(expression);

            if (e is ConstantExpression)
            {
                var constant = (ConstantExpression)e;
                if (constant.Value is bool && (bool)constant.Value)
                {
                    e = null;
                }
            }

            return new Tuple<Expression, List<string>>(e, Values);
        }

        protected override Expression VisitBinary(BinaryExpression binaryExpression)
        {
            var e = base.VisitBinary(binaryExpression);

            switch (e.NodeType)
            {
                case ExpressionType.And:
                    var andExpression = (BinaryExpression)e;

                    if (IsBooleanConstant(andExpression.Left))
                    {
                        return GetBooleanConstant(andExpression.Left) ? andExpression.Right : Expression.Constant(false);
                    }

                    if (IsBooleanConstant(andExpression.Right))
                    {
                        return GetBooleanConstant(andExpression.Right) ? andExpression.Left : Expression.Constant(false);
                    }

                    break;
                case ExpressionType.Equal:
                    var equalExpression = (BinaryExpression)e;

                    var name = (ConstantExpression) equalExpression.Left;
                    var value = (ConstantExpression) equalExpression.Right;

                    if (name != null && 
                        name.Value.ToString().ToUpper().Equals(Property))
                    {
                        Values.Add(value.Value.ToString());

                        return Expression.Constant(true);
                    }

                    break;
            }

            return binaryExpression;
        }

        protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression)
        {
            var m = (MethodCallExpression) base.VisitMethodCall(methodCallExpression);

            if (m.Method.Name == "Contains" && m.Arguments.Count == 1 && m.Object is ConstantExpression)
            {
                var constantExpression = (ConstantExpression) m.Object;
                if (constantExpression.Value.ToString().ToUpper().Equals(Property))
                {
                    var value = (ConstantExpression) m.Arguments[0];
                    Values.Add(value.Value.ToString());

                    return Expression.Constant(true);
                }
            }

            return m;
        }

        private static bool IsBooleanConstant(Expression e)
        {
            if (e is ConstantExpression)
            {
                var constant = (ConstantExpression)e;

                return constant.Value is bool;
            }

            return false;
        }

        private static bool GetBooleanConstant(Expression e)
        {
            if (e is ConstantExpression)
            {
                var constant = (ConstantExpression)e;

                return (bool)constant.Value;
            }

            throw new ArgumentException("Expression not ConstantExpression", "e");
        }
    }
}
